#include <iostream>
#include <string>

using namespace std;

// 成员子类
class stu1
{
private:
    /* data */
public:
    // 无参构造函数
    stu1(/* args */)
    {
        cout << "无参构造" << endl; // 输出 "无参构造"
    }

    // 有参构造函数
    stu1(int val)
    {
        cout << "有参构造 val = " << val << endl; // 输出 "有参构造 val = " 和传入的 val 值
    }
};

/*
使用初始化列表的条件 
    1、成员变量与构造函数实参重名
    2、成员变量中有const 
    3、成员变量中有 &
    4、有成员子类 并且需要调用成员子类的 有参构造
*/
class stu2
{
private:
    // 成员变量
    string & name; // 引用类型成员变量，必须在构造函数初始化列表中进行初始化
    const int id;  // 常量类型成员变量，必须在构造函数初始化列表中进行初始化

    // 成员子类
    stu1 stu;  // stu1 类型的成员变量
public:
    // 有参构造函数，初始化成员变量 name, id 和成员子类 stu
    stu2(string name , int id)
        : name(name), id(id), stu(30)  // 初始化列表：初始化成员变量和成员子类
    {
        // 这里没有额外的代码，因为所有的初始化操作已经在初始化列表中完成
    }
};

int main(int argc, char const *argv[])
{
    string str = "Alice"; // 为字符串变量 str 赋值
    stu2 s1(str, 10);     // 创建 stu2 类型的对象 s1，并传入 str 和 10
    return 0;  // 程序正常结束
}

/*
为什么 stu1 s2(); 会被解析为函数声明：
C++ 的解析规则允许声明一个返回类型为 stu1 并且不带参数的函数。这种语法 stu1 s2(); 是合法的函数声明形式，意味着声明一个名为 s2 的函数，返回类型是 stu1，并且该函数没有参数。

在 C++ 中，空的括号 () 常常被用来表示函数参数列表，C++ 编译器会将它优先识别为函数声明，而不是对象的初始化。因此，stu1 s2(); 会被当作一个函数声明而不是一个对象实例。
*/

/*
stu1 类:

定义了两个构造函数：一个无参构造函数和一个带有 int val 参数的有参构造函数。在构造时会输出相应的消息。
无参构造函数打印 "无参构造"，有参构造函数会输出 "有参构造 val = x"。
stu2 类:

stu2 类有三个成员变量：name（字符串引用类型）、id（常量整数）、以及 stu（stu1 类型的成员子类）。
构造函数中使用了初始化列表来初始化这些成员变量。初始化列表的顺序与成员变量声明的顺序相同。
初始化列表：

引用类型成员变量 (name): 引用类型必须在构造函数初始化列表中进行初始化。
常量成员变量 (id): 常量成员变量必须在构造函数初始化列表中进行初始化，因为常量在声明时不能被修改。
成员子类 (stu): 在 stu2 的构造函数初始化列表中，stu(10) 会调用 stu1 的有参构造函数，并传入 10 作为参数。
main 函数：

在 main 函数中，创建了一个 string 类型的变量 str，并初始化为 "Alice"。
然后，创建了一个 stu2 类型的对象 s1，并传入 str 和 10 作为参数。
*/